// 版权归Epic Games, Inc.所有。保留所有权利。

#include "LyraInputModifiers.h"

#include "EnhancedPlayerInput.h"
#include "GameFramework/PlayerController.h"
#include "Input/LyraAimSensitivityData.h"
#include "Player/LyraLocalPlayer.h"
#include "Settings/LyraSettingsShared.h"

#include UE_INLINE_GENERATED_CPP_BY_NAME(LyraInputModifiers)

DEFINE_LOG_CATEGORY_STATIC(LogLyraInputModifiers, Log, All); // 定义输入修改器日志类别

//////////////////////////////////////////////////////////////////////
// LyraInputModifiersHelpers

namespace LyraInputModifiersHelpers
{
	/** 返回增强玩家输入指针的所有者LyraLocalPlayer */
	static ULyraLocalPlayer* GetLocalPlayer(const UEnhancedPlayerInput* PlayerInput)
	{
		if (PlayerInput) // 确保玩家输入有效
		{
			// 获取外部对象并转换为玩家控制器
			if (APlayerController* PC = Cast<APlayerController>(PlayerInput->GetOuter()))
			{
				return Cast<ULyraLocalPlayer>(PC->GetLocalPlayer()); // 返回本地玩家
			}
		}
		return nullptr; // 返回空指针
	}
	
}

//////////////////////////////////////////////////////////////////////
// ULyraSettingBasedScalar

/**
 * 修改原始输入值的实现。
 * @param PlayerInput 增强玩家输入指针
 * @param CurrentValue 当前输入值
 * @param DeltaTime 增量时间
 * @return 修改后的输入值
 */
FInputActionValue ULyraSettingBasedScalar::ModifyRaw_Implementation(const UEnhancedPlayerInput* PlayerInput, FInputActionValue CurrentValue, float DeltaTime)
{
	// 确保当前值不是布尔类型
	if (ensureMsgf(CurrentValue.GetValueType() != EInputActionValueType::Boolean, TEXT("基于设置的标量修改器不支持布尔值。")))
	{
		// 获取本地玩家
		if (ULyraLocalPlayer* LocalPlayer = LyraInputModifiersHelpers::GetLocalPlayer(PlayerInput))
		{
			const UClass* SettingsClass = ULyraSettingsShared::StaticClass(); // 获取设置类
			ULyraSettingsShared* SharedSettings = LocalPlayer->GetSharedSettings(); // 获取共享设置
			
			const bool bHasCachedProperty = PropertyCache.Num() == 3; // 检查是否有缓存的属性
			
			// 查找或使用缓存的属性
			const FProperty* XAxisValue = bHasCachedProperty ? PropertyCache[0] : SettingsClass->FindPropertyByName(XAxisScalarSettingName);
			const FProperty* YAxisValue = bHasCachedProperty ? PropertyCache[1] : SettingsClass->FindPropertyByName(YAxisScalarSettingName);
			const FProperty* ZAxisValue = bHasCachedProperty ? PropertyCache[2] : SettingsClass->FindPropertyByName(ZAxisScalarSettingName);

			// 如果属性缓存为空，填充缓存
			if (PropertyCache.IsEmpty())
			{
				PropertyCache.Emplace(XAxisValue);
				PropertyCache.Emplace(YAxisValue);
				PropertyCache.Emplace(ZAxisValue);
			}

			FVector ScalarToUse = FVector(1.0, 1.0, 1.0); // 默认缩放向量

			// 根据输入值类型应用不同的缩放
			switch (CurrentValue.GetValueType())
			{
			case EInputActionValueType::Axis3D:
				ScalarToUse.Z = ZAxisValue ? *ZAxisValue->ContainerPtrToValuePtr<double>(SharedSettings) : 1.0; // 获取Z轴缩放值
				//[[fallthrough]]; //  fallthrough属性
			case EInputActionValueType::Axis2D:
				ScalarToUse.Y = YAxisValue ? *YAxisValue->ContainerPtrToValuePtr<double>(SharedSettings) : 1.0; // 获取Y轴缩放值
				//[[fallthrough]]; //  fallthrough属性
			case EInputActionValueType::Axis1D:
				ScalarToUse.X = XAxisValue ? *XAxisValue->ContainerPtrToValuePtr<double>(SharedSettings) : 1.0; // 获取X轴缩放值
				break;
			}

			// 钳制缩放值在最小和最大范围内
			ScalarToUse.X = FMath::Clamp(ScalarToUse.X, MinValueClamp.X, MaxValueClamp.X);
			ScalarToUse.Y = FMath::Clamp(ScalarToUse.Y, MinValueClamp.Y, MaxValueClamp.Y);
			ScalarToUse.Z = FMath::Clamp(ScalarToUse.Z, MinValueClamp.Z, MaxValueClamp.Z);
			
			return CurrentValue.Get<FVector>() * ScalarToUse; // 返回缩放后的值
		}
	}
	
	return CurrentValue;	// 返回原始值
}

//////////////////////////////////////////////////////////////////////
// ULyraInputModifierDeadZone

/**
 * 修改原始输入值的实现，应用死区处理。
 * @param PlayerInput 增强玩家输入指针
 * @param CurrentValue 当前输入值
 * @param DeltaTime 增量时间
 * @return 修改后的输入值
 */
FInputActionValue ULyraInputModifierDeadZone::ModifyRaw_Implementation(const UEnhancedPlayerInput* PlayerInput, FInputActionValue CurrentValue, float DeltaTime)
{
	EInputActionValueType ValueType = CurrentValue.GetValueType(); // 获取输入值类型
	ULyraLocalPlayer* LocalPlayer = LyraInputModifiersHelpers::GetLocalPlayer(PlayerInput); // 获取本地玩家
	// 如果是布尔类型或没有本地玩家，直接返回当前值
	if (ValueType == EInputActionValueType::Boolean || !LocalPlayer)
	{
		return CurrentValue;
	}
	
	ULyraSettingsShared* Settings = LocalPlayer->GetSharedSettings(); // 获取共享设置
	ensure(Settings); // 确保设置有效

	// 根据死区摇杆类型获取下阈值
	float LowerThreshold =
		(DeadzoneStick == EDeadzoneStick::MoveStick) ? 
		Settings->GetGamepadMoveStickDeadZone() : // 移动摇杆死区
		Settings->GetGamepadLookStickDeadZone(); // 视角摇杆死区
	
	LowerThreshold = FMath::Clamp(LowerThreshold, 0.0f, 1.0f); // 钳制下阈值在0到1之间
	
	// 死区处理lambda函数
	auto DeadZoneLambda = [LowerThreshold, this](const float AxisVal)
	{
		// 我们需要在移除死区后将输入转换并缩放到+/-1范围。
		return FMath::Min(1.f, (FMath::Max(0.f, FMath::Abs(AxisVal) - LowerThreshold) / (UpperThreshold - LowerThreshold))) * FMath::Sign(AxisVal);
	};

	FVector NewValue = CurrentValue.Get<FVector>(); // 获取当前值的向量形式
	// 根据死区类型应用不同的处理
	switch (Type)
	{
	case EDeadZoneType::Axial:
		NewValue.X = DeadZoneLambda(NewValue.X); // 处理X轴
		NewValue.Y = DeadZoneLambda(NewValue.Y); // 处理Y轴
		NewValue.Z = DeadZoneLambda(NewValue.Z); // 处理Z轴
		break;
	case EDeadZoneType::Radial:
		if (ValueType == EInputActionValueType::Axis3D)
		{
			NewValue = NewValue.GetSafeNormal() * DeadZoneLambda(NewValue.Size()); // 3D径向处理
		}
		else if (ValueType == EInputActionValueType::Axis2D)
		{
			NewValue = NewValue.GetSafeNormal2D() * DeadZoneLambda(NewValue.Size2D()); // 2D径向处理
		}
		else
		{
			NewValue.X = DeadZoneLambda(NewValue.X); // 1D轴向处理
		}
		break;
	}

	return NewValue; // 返回处理后的值
}

/**
 * 获取可视化颜色的实现。
 * @param SampleValue 采样值
 * @param FinalValue 最终值
 * @return 可视化颜色
 */
FLinearColor ULyraInputModifierDeadZone::GetVisualizationColor_Implementation(FInputActionValue SampleValue, FInputActionValue FinalValue) const
{
	// 取自UInputModifierDeadZone::GetVisualizationColor_Implementation
	// 如果是布尔或1D轴类型
	if (FinalValue.GetValueType() == EInputActionValueType::Boolean || FinalValue.GetValueType() == EInputActionValueType::Axis1D)
	{
		return FLinearColor(FinalValue.Get<float>() == 0.f ? 1.f : 0.f, 0.f, 0.f); // 返回红色或黑色
	}
	// 2D轴类型，根据X和Y轴是否为0返回不同强度的红色
	return FLinearColor((FinalValue.Get<FVector2D>().X == 0.f ? 0.5f : 0.f) + (FinalValue.Get<FVector2D>().Y == 0.f ? 0.5f : 0.f), 0.f, 0.f);
}

//////////////////////////////////////////////////////////////////////
// ULyraInputModifierGamepadSensitivity

/**
 * 修改原始输入值的实现，应用游戏手柄灵敏度。
 * @param PlayerInput 增强玩家输入指针
 * @param CurrentValue 当前输入值
 * @param DeltaTime 增量时间
 * @return 修改后的输入值
 */
FInputActionValue ULyraInputModifierGamepadSensitivity::ModifyRaw_Implementation(const UEnhancedPlayerInput* PlayerInput, FInputActionValue CurrentValue, float DeltaTime)
{
	// 无法缩放布尔动作类型
	ULyraLocalPlayer* LocalPlayer = LyraInputModifiersHelpers::GetLocalPlayer(PlayerInput); // 获取本地玩家
	// 如果是布尔类型、没有本地玩家或没有灵敏度级别表，直接返回当前值
	if (CurrentValue.GetValueType() == EInputActionValueType::Boolean || !LocalPlayer || !SensitivityLevelTable)
	{
		return CurrentValue;
	}
	
	ULyraSettingsShared* Settings = LocalPlayer->GetSharedSettings(); // 获取共享设置
	ensure(Settings); // 确保设置有效

	// 根据瞄准类型获取游戏手柄灵敏度预设
	const ELyraGamepadSensitivity Sensitivity = (TargetingType == ELyraTargetingType::Normal) ? Settings->GetGamepadLookSensitivityPreset() : Settings->GetGamepadTargetingSensitivityPreset();

	const float Scalar = SensitivityLevelTable->SensitivtyEnumToFloat(Sensitivity); // 将灵敏度枚举转换为浮点数

	return CurrentValue.Get<FVector>() * Scalar; // 返回缩放后的值
}

//////////////////////////////////////////////////////////////////////
// ULyraInputModifierAimInversion

/**
 * 修改原始输入值的实现，应用瞄准反转。
 * @param PlayerInput 增强玩家输入指针
 * @param CurrentValue 当前输入值
 * @param DeltaTime 增量时间
 * @return 修改后的输入值
 */
FInputActionValue ULyraInputModifierAimInversion::ModifyRaw_Implementation(const UEnhancedPlayerInput* PlayerInput, FInputActionValue CurrentValue, float DeltaTime)
{
	ULyraLocalPlayer* LocalPlayer = LyraInputModifiersHelpers::GetLocalPlayer(PlayerInput); // 获取本地玩家
	if (!LocalPlayer) // 如果没有本地玩家，直接返回当前值
	{
		return CurrentValue;
	}
	
	ULyraSettingsShared* Settings = LocalPlayer->GetSharedSettings(); // 获取共享设置
	ensure(Settings); // 确保设置有效

	FVector NewValue = CurrentValue.Get<FVector>(); // 获取当前值的向量形式
	
	// 如果启用了垂直轴反转，反转Y轴
	if (Settings->GetInvertVerticalAxis())
	{
		NewValue.Y *= -1.0f;
	}
	
	// 如果启用了水平轴反转，反转X轴
	if (Settings->GetInvertHorizontalAxis())
	{
		NewValue.X *= -1.0f;
	}
	
	return NewValue; // 返回修改后的值
}